/*
 * Copyright 2008 - 2009 Lars Heuer (heuer[at]semagia.com). All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.semagia.atomico.server.impl.restlet.resources;

import java.util.List;

import org.restlet.data.Method;
import org.restlet.data.Status;
import org.restlet.representation.Variant;
import org.restlet.resource.ResourceException;
import org.restlet.resource.ServerResource;

import com.semagia.atomico.MediaType;
import com.semagia.atomico.server.IConfiguration;
import com.semagia.atomico.server.dm.ICollectionInfo;
import com.semagia.atomico.server.dm.IFragmentInfo;
import com.semagia.atomico.server.dm.ISnapshotInfo;
import com.semagia.atomico.server.impl.restlet.AtomicoServerApplication;
import com.semagia.atomico.server.impl.restlet.IRestletServerConstants;
import com.semagia.atomico.server.storage.IStorage;
import com.semagia.atomico.server.storage.StorageException;

/**
 * Commmon base class for all resources.
 * <p>
 * This resource provides some convenient functions and provides access to
 * a {@link IStorage} instance.
 * </p>
 * 
 * @author Lars Heuer (heuer[at]semagia.com) <a href="http://www.semagia.com/">Semagia</a>
 * @version $Rev: 66 $ - $Date: 2010-09-06 15:29:07 +0000 (Mon, 06 Sep 2010) $
 */
abstract class AbstractBaseResource extends ServerResource implements
        IRestletServerConstants {

    /**
     * The collection info to operate on.
     * Might be <tt>null</tt>.
     */
    protected ICollectionInfo _collInfo;

    /**
     * The storage to operate on.
     */
    protected final IStorage _storage;

    /**
     * Creates a base resource.
     */
    protected AbstractBaseResource() {
        super();
        _storage = getApplication().getStorage();
        super.setAnnotated(false);
    }

    /**
     * Initializes the {@link #_collInfo}.
     * <p>
     * Side effects: If the collection info is null, the resource is marked as
     * non-existent and the status is set to 404.
     * </p> 
     *
     * @throws ResourceException
     */
    protected final void initializeCollectionInfo() throws ResourceException {
        initializeCollectionInfo(true);
    }

    /**
     * Initializes the collection information.
     *
     * @param mustExist Indicates if the collection must exists.
     * @throws ResourceException In case of a {@link StorageException} or if 
     *      <tt>mustExists</tt> is <tt>true</tt> and the collection does not
     *      exist.
     */
    protected final void initializeCollectionInfo(boolean mustExist) throws ResourceException {
        final String collId = (String) getRequest().getAttributes().get(COLLECTION_ID);
        try {
            _collInfo = collId == null ? null : _storage.getCollectionInfo(collId);
        }
        catch (StorageException ex) {
            throw new ResourceException(ex);
        }
        if (mustExist && _collInfo == null) {
            throw new ResourceException(Status.CLIENT_ERROR_NOT_FOUND);
        }
    }

    /* (non-Javadoc)
     * @see org.restlet.resource.ServerResource#isExisting()
     */
    @Override
    public boolean isExisting() {
        return _collInfo != null;
    }

    /* (non-Javadoc)
     * @see org.restlet.Handler#getApplication()
     */
    @Override
    public AtomicoServerApplication getApplication() {
        return (AtomicoServerApplication) super.getApplication();
    }

    /**
     * Returns the time when the resource was modified.
     *
     * @return The time when this resource was modified.
     */
    protected abstract long lastModification();

    /**
     * Returns the application-specific configuration.
     *
     * @return The configuration instance.
     */
    protected final IConfiguration getConfiguration() {
        return getApplication().getConfiguration();
    }

    /**
     * Returns the request URL / IRI as string.
     *
     * @return The request IRI as string.
     */
    protected final String getRequestIRI() {
        return getRequest().getResourceRef().toString();
    }

    protected abstract Iterable<MediaType> getMediaTypes();

    /* (non-Javadoc)
     * @see org.restlet.resource.ServerResource#getVariants(org.restlet.data.Method)
     */
    @Override
    public List<Variant> getVariants(Method method) {
        if (Method.GET.equals(method)) {
            for (MediaType mt: getMediaTypes()) {
                super.getVariants().put(method, new ImmutableVariant(MediaTypeUtils.toRestletMediaType(mt)));
            }
        }
        return super.getVariants(method);
    }

    private static String _getSnapshotsPath() {
        return "snapshots";
    }

    private static String _getFragmentsPath() {
        return "fragments";
    }

    /**
     * Returns the root.
     *
     * @return
     */
    private String _getRoot() {
        return getRequest().getRootRef().toString();
    }

    /**
     * 
     *
     * @param collId
     * @return
     */
    public String linkToCollection(String collId) {
        return _linkToCollection(collId, new StringBuilder(collId.length()+2)).toString();
    }

    /**
     * 
     *
     * @param collId
     * @param buff
     * @return
     */
    private StringBuilder _linkToCollection(String collId, StringBuilder buff) {
        buff.append('/')
            .append(collId)
            .append('/');
        return buff;
    }

    /**
     * 
     *
     * @param collId
     * @return
     */
    public String linkToSnapshots(String collId) {
        return _linkToSubResource(collId, "snapshots");
    }

    /**
     * 
     *
     * @param collId
     * @param snapshotId
     * @return
     */
    public String linkToSnapshot(String collId, String snapshotId) {
        final String path = _getSnapshotsPath();
        StringBuilder buff = new StringBuilder(collId.length() + path.length() + snapshotId.length() + 4);
        _linkToSubResource(collId, path, buff)
            .append(snapshotId);
        return buff.toString();
    }

    /**
     * 
     *
     * @param collId
     * @return
     */
    public String linkToFragments(String collId) {
        return _linkToSubResource(collId, _getFragmentsPath());
    }

    /**
     * 
     *
     * @param collId
     * @param subPath
     * @return
     */
    private String _linkToSubResource(String collId, String subPath) {
        return _linkToSubResource(collId, subPath, 
                new StringBuilder(collId.length() + subPath.length() + 4)).toString();
    }

    /**
     * 
     *
     * @param collId
     * @param subPath
     * @param buff
     * @return
     */
    private StringBuilder _linkToSubResource(String collId, String subPath, StringBuilder buff) {
        _linkToCollection(collId, buff)
            .append(subPath)
            .append('/');
        return buff;
    }

    /**
     * 
     *
     * @param collId
     * @param fragmentId
     * @return
     */
    public String linkToFragment(String collId, String fragmentId) {
        final String path = _getFragmentsPath(); 
        StringBuilder buff = new StringBuilder(collId.length() + path.length() + fragmentId.length() + 4);
        _linkToSubResource(collId, path, buff)
            .append(fragmentId);
        return buff.toString();
    }


    /**
     * Returns a link to the snapshots feed.
     *
     * @return The link to the snapshots feed.
     * @throws ResourceException If this resource didn't find the collection 
     *          to operate upon.
     */
    protected final String linkToSnapshots() throws ResourceException {
        if (_collInfo == null) {
            throw new ResourceException(Status.SERVER_ERROR_INTERNAL);
        }
        return _getRoot()
                + linkToSnapshots(_collInfo.getCollectionId());
    }

    /**
     * Returns the link to the fragments feed.
     *
     * @return The link to the fragments feed.
     * @throws ResourceException If this resource didn't find the collection 
     *          to operate upon. 
     */
    protected final String linkToFragments() throws ResourceException {
        if (_collInfo == null) {
            throw new ResourceException(Status.SERVER_ERROR_INTERNAL);
        }
        return _getRoot()
                + linkToFragments(_collInfo.getCollectionId());
    }

    /**
     * Returns the link to the underlying collection.
     *
     * @return A link to the collection.
     * @throws ResourceException If there is no underlying collection.
     */
    protected final String linkToCollection() throws ResourceException {
        return linkToCollection(_collInfo);
    }

    /**
     * Returns the link to the provided <tt>collectionInfo</tt>.
     *
     * @param collectionInfo The collection info to generate a link for.
     * @return The link to the specified collection.
     * @throws ResourceException In case the collection does not exist.
     */
    protected final String linkToCollection(ICollectionInfo collectionInfo) throws ResourceException {
        if (collectionInfo == null) {
            throw new ResourceException(Status.SERVER_ERROR_INTERNAL);
        }
        return _getRoot()
                + linkToCollection(collectionInfo.getCollectionId());
    }

    /**
     * 
     *
     * @param collInfo
     * @param info
     * @return
     * @throws ResourceException 
     */
    protected final String linkTo(final ISnapshotInfo info) throws ResourceException {
        if (_collInfo == null) {
            throw new ResourceException(Status.SERVER_ERROR_INTERNAL);
        }
        return _getRoot()
                + linkToSnapshot(
                        _collInfo.getCollectionId(), info.getSnapshotId());
    }

    /**
     * 
     *
     * @param collInfo
     * @param info
     * @return
     * @throws ResourceException 
     */
    protected final String linkTo(final IFragmentInfo info) throws ResourceException {
        if (_collInfo == null) {
            throw new ResourceException(Status.SERVER_ERROR_INTERNAL);
        }
        return _getRoot()
                + linkToFragment(
                        _collInfo.getCollectionId(), info.getFragmentId());
    }

}
